include!("data.rs");

const CHECKSUM_FACTOR: &[u16] = &[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const CHECKSUM_CODE: &[char] = &['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

const ERR_MSG: &str = "身份证号码错误";

fn match_area(code: &str) -> Option<String> {
    let l1_code = format!("{}0000", &code[0..=1]);
    let l1_str = match AREA_CODE.get(l1_code.as_str()) {
        None => return None,
        Some(v) => *v,
    };
    let l2_code = format!("{}00", &code[0..=3]);
    let l2_str = if l2_code == l1_code {
        "市辖区"
    } else {
        match AREA_CODE.get(l2_code.as_str()) {
            None => return None,
            Some(v) => *v,
        }
    };
    let l3_str = match AREA_CODE.get(&code[0..=5]) {
        None => return None,
        Some(v) => *v,
    };
    Some(format!("{}-{}-{}", l1_str, l2_str, l3_str))
}

fn checksum_18(code: &str) -> bool {
    let mut sum = 0;
    let id_card_no_vec = code.chars().collect::<Vec<char>>();
    for (i, c) in id_card_no_vec.iter().enumerate().take(17) {
        let c = match c {
            '0'..='9' => *c as u16 - '0' as u16,
            _ => return false,
        };
        sum += CHECKSUM_FACTOR[i] * c;
    }
    id_card_no_vec
        .get(17)
        .map(|&c| c == CHECKSUM_CODE[(sum % 11) as usize])
        .unwrap_or(false)
}

fn match_sexual(code: &str) -> &'static str {
    let sexual = if code.len() == 18 {
        code.as_bytes()[16]
    } else {
        code.as_bytes()[14]
    };
    if (sexual - '0' as u8) % 2 == 0 {
        "女"
    } else {
        "男"
    }
}

fn match_date(code: &str) -> String {
    if code.len() == 18 {
        format!("{}年{}月{}日", &code[6..10], &code[10..12], &code[12..14])
    } else {
        let yy = code[6..8].parse::<u16>().unwrap_or(0);
        if yy > 20 {
            format!("19{}年{}月{}日", &code[6..8], &code[8..10], &code[10..12])
        } else {
            format!("20{}年{}月{}日", &code[6..8], &code[8..10], &code[10..12])
        }
    }
}
pub fn check_id_card(id_card_no: &str) -> String {
    if id_card_no.len() != 18 && id_card_no.len() != 15 {
        return ERR_MSG.to_string();
    }
    if id_card_no.len() == 18 && !checksum_18(id_card_no) {
        return ERR_MSG.to_string();
    }
    let area = match match_area(&id_card_no[0..6]) {
        Some(v) => v,
        None => return ERR_MSG.to_string(),
    };
    let sexual = match_sexual(id_card_no);
    let date = match_date(id_card_no);
    format!("身份证号码正确,{sexual},{date},{area}")
}
